package com.young.aidl_base_server;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.IInterface;
import android.os.ParcelFileDescriptor;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;

import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;

import com.young.aidl_base_sdk.ICalculator;
import com.young.aidl_base_sdk.ICalculatorListener;
import com.young.aidl_base_sdk.bean.Sample;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicIntegerArray;

/**
 * Author：young
 * .
 * Time： 13/9/2023 上午9:42 周三
 * .
 * Desc：计算的服务，在这里调用 aidl 的接口
 */
public class CalculatorService extends Service {

    public static final String TAG = "Young-AIDL-" + CalculatorService.class.getSimpleName();

    // 用一个集合换成监听对象，这里为了线程安全，使用 CopyOnWriteArrayList
//        private final List<ICalculatorListener> mListeners = new CopyOnWriteArrayList<>();
    public final static ConcurrentMap<Integer, ICalculatorListener> mListenerMap = new ConcurrentHashMap<>();

    /**
     * 这个是远程监听回调集合，不需要手动释放listener，客户端的进程结束，就会自动释放客户端回调
     */
    public final static MyRemoteCallbackList<ICalculatorListener> mRemoteCallbackList = new MyRemoteCallbackList<>();


    /**
     * 遍历通知客户端
     *
     * beginBroadcast 和 finishBroadcast 是成对出现的，防止
     * 出现连续调用 beginBroadcast 方法，加个同步锁
     *
     */
    public synchronized static void notifyClients() {
        int i = mRemoteCallbackList.beginBroadcast();
        for (int j = 0; j < i; j++) {
            try {
                mRemoteCallbackList.getBroadcastItem(j).onCallback("notifyClients = tiankon "+i);
            } catch (RemoteException e) {
                throw new RuntimeException(e);
            }
        }
        mRemoteCallbackList.finishBroadcast();
    }


    public int mNum = 10;

    private final ICalculator.Stub mCalculator = new ICalculator.Stub() {


        @Override
        public int add(int a, int b) throws RemoteException {
            return mNum;
        }

        @Override
        public int subtract(int a, int b) throws RemoteException {
            return 0;
        }

        @Override
        public int multiply(int a, int b) throws RemoteException {
            return 0;
        }

        @Override
        public int divide(int a, int b) throws RemoteException {
            return 0;
        }

        @Override
        public void optionParcel(Sample sample) throws RemoteException {
            String processName = CalculatorService.this.getApplicationInfo().processName;
            Log.e(TAG, "hi pid=" + processName + " shenxian = " + MainActivity.num + " " + sample);
        }

        @Override
        public void optionOut(Sample sample) throws RemoteException {
            Log.e(TAG, "optionOut = " + sample);
            sample.setNum(89898);
        }

        @Override
        public void optionBundle(Bundle bundle) throws RemoteException {
            // 如果是 bundle 参数，必须设置下面的 setClassLoader 方法，否则会抛出异常
            // 不设置的话，可能无法准确构建 Parcelable、Serializable 对象
            bundle.setClassLoader(getClassLoader());
            Log.e(TAG, "optionBundle1 = " + bundle.getParcelable("key1"));
            Log.e(TAG, "optionBundle1 = " + bundle.getSerializable("key2"));

        }

        @Override
        public void transactFileDescriptor(ParcelFileDescriptor pdf) throws RemoteException {
            Log.i(TAG, "transactFileDescriptor pid = " + Binder.getCallingPid() + " uid = " + Binder.getCallingUid());
            File file = new File(getCacheDir(), "file.avi");
            try (ParcelFileDescriptor.AutoCloseInputStream inputStream = new ParcelFileDescriptor.AutoCloseInputStream(pdf)) {
                file.delete();
                file.createNewFile();
                FileOutputStream outputStream = new FileOutputStream(file);
                byte[] buffer = new byte[1024];
                int len;
                while ((len = inputStream.read(buffer)) != -1) {
                    outputStream.write(buffer, 0, len);
                }
                outputStream.close();
                pdf.close();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                Log.e(TAG, "服务端 开始接收大文件 " + file.getName() + " " + file.length() + " " + System.currentTimeMillis());
            }
        }

        @Override
        public void optionTimeOut() throws RemoteException {
            try {
                Thread.sleep(10 * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void optionOneWay() throws RemoteException {
            mNum = 10230;
            Log.e(TAG, "optionOneWAy = " + Thread.currentThread().getName()+" mNum = "+mNum);
            try {
                Thread.sleep(10 * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }


        @Override
        public void registerListener(ICalculatorListener listener) throws RemoteException {
            // 这里接收的 listener 和 unRegisterListener 接收的 listener ，客户端虽然传递的对象虽然相同，
            // 但是到了服务端的时候就不相同了，所以需要调用 .asBinder() 方法来保证是同一个监听对象
            IBinder iBinder = listener.asBinder();
            Log.i(TAG, "registerListener = " + iBinder.hashCode());
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            mListenerMap.put(iBinder.hashCode(), listener);
//            mListenerMap.get(iBinder.hashCode()).onCallback("hello");

//            mListeners.add(listener);
//            for (ICalculatorListener iCalculatorListener : mListeners) {
//                if (iCalculatorListener.asBinder() == listener.asBinder()) {
//                    iCalculatorListener.onCallback("tian kong fang");
//                }
//            }

            listener.onCallback("我是服务器主动返回的数据呀");
//            mRemoteCallbackList.register(listener);
            mRemoteCallbackList.register(listener,"tiankong");
        }

        @Override
        public void unRegisterListener(ICalculatorListener listener) throws RemoteException {
            IBinder iBinder = listener.asBinder();
            Log.i(TAG, "unRegisterListener = " + iBinder.hashCode());


            Log.i(TAG, "unRegisterListener before = " + mListenerMap.size());
            mListenerMap.remove(iBinder.hashCode());
            Log.i(TAG, "unRegisterListener after= " + mListenerMap.size());

//            Log.i(TAG, "unRegisterListener iCalculatorListener before = " + mListeners.size());
//            ICalculatorListener targetListener = null;
//            for (ICalculatorListener iCalculatorListener : mListeners) {
//                if (iCalculatorListener.asBinder() == listener.asBinder()) {
//                    targetListener = iCalculatorListener;
//                }
//            }
//            if (targetListener != null) {
//                mListeners.remove(targetListener);
//            }
//
//            Log.i(TAG, "unRegisterListener iCalculatorListener after = " + mListeners.size());
            mRemoteCallbackList.unregister(listener);
            // 打印监听回调的个数
            Log.d(TAG, "unRegisterListener: " + mRemoteCallbackList.getRegisteredCallbackCount());
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        Log.e(TAG, "onCreate:");
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            // 在 Android8.0 及之后的系统中，需要使用Notification将service设置有前台service，
            // 否则会抛出异常
            startServiceForeground();
        }
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.e(TAG, "onBind: " + intent.getAction());
        return mCalculator;
    }

    private static final String CHANNEL_ID_STRING = "com.young.aidl_base_server";
    private static final int CHANNEL_ID = 0x11;

    @RequiresApi(api = Build.VERSION_CODES.O)
    private void startServiceForeground() {
        NotificationManager notificationManager = (NotificationManager)
                getSystemService(Context.NOTIFICATION_SERVICE);
        NotificationChannel channel;
        channel = new NotificationChannel(CHANNEL_ID_STRING, getString(R.string.app_name),
                NotificationManager.IMPORTANCE_LOW);
        notificationManager.createNotificationChannel(channel);
        Notification notification = new Notification.Builder(getApplicationContext(),
                CHANNEL_ID_STRING).build();
        startForeground(CHANNEL_ID, notification);
    }
}
